package com.googlecode.animator.core
{
	import com.googlecode.animator.data.Notifications;
	import com.googlecode.animator.data.cameras.ActualViewCameraVO;
	import com.googlecode.animator.data.markers.GlobalMarkerVO;
	import com.googlecode.animator.data.objects3d.CameraProxyVO;
	import com.googlecode.animator.data.objects3d.CubeVO;
	import com.googlecode.animator.data.objects3d.Object3DVO;
	import com.googlecode.animator.data.objects3d.PlaneVO;
	import com.googlecode.animator.data.objects3d.SphereVO;
	import com.googlecode.animator.data.objects3d.TargetProxyVO;
	import com.googlecode.animator.model.Animator3DPlayerProxy;
	import com.googlecode.animator.services.AnimationTimer;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	
	import mx.collections.ArrayCollection;
	import mx.collections.Sort;
	import mx.collections.SortField;
	
	import org.puremvc.as3.multicore.interfaces.INotification;
	import org.puremvc.as3.multicore.interfaces.IProxy;
	import org.puremvc.as3.multicore.patterns.mediator.Mediator;

	/**
	 * Ties in the WebPlayer to PureMVC. 
	 * @author USER
	 * 
	 */
	public class Animator3DPlayerMediator extends Mediator
	{
		
		public static const NAME:String = "WebPlayerMediator";
		
		private var _path:String = "project.xml";
		
		private var _projectXML:XML;
		
		private var _timer:AnimationTimer;
		
		private var _count:uint;
		
		private var _increment:uint;
		
		private var _globalMarkers:ArrayCollection;
		
		private var _maxTime:uint;
		
		/**
		 * Constructor. 
		 * @param viewComponent
		 * 
		 */
		public function Animator3DPlayerMediator(viewComponent:Animator3DPlayer)
		{
			super(NAME, viewComponent);
			
			viewComponent.addEventListener("prev", onPrev);
			viewComponent.addEventListener("play", onPlay);
			viewComponent.addEventListener("stop", onStop);
			viewComponent.addEventListener("next", onNext);
			
			_timer = new AnimationTimer();
			_timer.addEventListener(AnimationTimer.EVENT, onTimer);
			
			_increment = 0;
			
		}
		
		/**
		 * Link in with PureMVC framework. 
		 * @return 
		 * 
		 */
		override public function listNotificationInterests():Array
		{
			var list:Array = super.listNotificationInterests();
			
			list.push(Notifications.VIEWPORT_ADDED);
			list.push(Notifications.SCENE_ADDED);
			
			return list;
		}
		
		/**
		 * Handle notifications. 
		 * @param notification
		 * 
		 */
		override public function handleNotification(notification:INotification):void
		{
			switch(notification.getName())
			{
				case Notifications.VIEWPORT_ADDED:
				
					// ensure the 3D scene exists
					sendNotification(Notifications.ADD_SCENE);
					
					break;
					
				case Notifications.SCENE_ADDED:
				
					handleSceneAdded();
					
					break;
			}
		}
		
		/**
		 * Once the scene has been added the 3D world can be built from XML. 
		 * 
		 */
		protected function handleSceneAdded():void
		{
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onProjectXMLLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, onError);

			var url:URLRequest = new URLRequest(_path);
			loader.load(url);
			
		}
		
		/**
		 * Called once the XML has been loaded. 
		 * @param evt
		 * 
		 */
		protected function onProjectXMLLoaded(evt:Event):void
		{
			(evt.target as URLLoader).removeEventListener(Event.COMPLETE, onProjectXMLLoaded);
			(evt.target as URLLoader).removeEventListener(IOErrorEvent.IO_ERROR, onError);
			
			_projectXML = new XML(((evt.target) as URLLoader).data);
			
			loadProject();
			
		}
		
		/**
		 * The main work is done here. Data from XML is converted into VOs and added
		 * to the model. 
		 * 
		 */
		protected function loadProject():void
		{
			// set up the viewport
			(viewComponent as Animator3DPlayer).stageWidth = _projectXML.stage.stageWidth;
			(viewComponent as Animator3DPlayer).stageHeight = _projectXML.stage.stageHeight;
			
			// load global markers
			var globalMarker:GlobalMarkerVO;
			_globalMarkers = new ArrayCollection();
			
			for each(var gMarker:XML in _projectXML.globalMarkers.globalMarker)
			{
				globalMarker = new GlobalMarkerVO();
				globalMarker.fromXML(gMarker);
				
				// get a permanent reference to globalMarkers to save having to look them up all the time
				// they cannot change once loaded
				_globalMarkers.addItem(globalMarker);
				
				sendNotification(Notifications.ADD_GLOBAL_MARKER, globalMarker);
			}
			
			//Sort keyFrames time ascending
			var s:Sort = new Sort();
			var sF:SortField = new SortField("time", true);
			sF.numeric = true;				
			s.fields = [sF];
			_globalMarkers.sort = s;
			_globalMarkers.refresh();
			
			// get a permanent reference to maxTime
			_maxTime = settings.maxTime;
			
			// load the actual view camera.
			var camera:ActualViewCameraVO = new ActualViewCameraVO();
			camera.fromXML(_projectXML.cameras.elements(ActualViewCameraVO.TYPE)[0]);
			
			sendNotification(Notifications.ADD_CAMERA, camera);
			
			// load the objects3D
			var o3D:Object3DVO
			
			for each(var object3D:XML in _projectXML.objects3D.*)
			{
				if(object3D.name() == CameraProxyVO.TYPE)
				{
					o3D = new CameraProxyVO();
					o3D.fromXML(object3D);

					sendNotification(Notifications.ADD_OBJECT3D, o3D as CameraProxyVO);	
				}
				
				if(object3D.name() == TargetProxyVO.TYPE)
				{
					o3D = new TargetProxyVO();
					o3D.fromXML(object3D);

					sendNotification(Notifications.ADD_OBJECT3D, o3D as TargetProxyVO);	
				}
				
				if(object3D.name() == SphereVO.TYPE)
				{
					o3D = new SphereVO();
					o3D.fromXML(object3D);
					
					sendNotification(Notifications.ADD_OBJECT3D, o3D as SphereVO);
				}
				
				if(object3D.name() == PlaneVO.TYPE)
				{
					o3D = new PlaneVO();
					o3D.fromXML(object3D);
					
					sendNotification(Notifications.ADD_OBJECT3D, o3D as PlaneVO);
				} 
				
				if(object3D.name() == CubeVO.TYPE)
				{
					o3D = new CubeVO();
					o3D.fromXML(object3D);
					
					sendNotification(Notifications.ADD_OBJECT3D, o3D as CubeVO);	
				}
			}
			
		}
		
		/**
		 * Called if there is an error in loading the XML. 
		 * @param evt
		 * 
		 */
		protected function onError(evt:IOErrorEvent):void
		{
			trace(evt.text);
		}
		
		/**
		 * Responds to a user clicking on the Previous button. 
		 * @param evt
		 * 
		 */
		protected function onPrev(evt:Event):void
		{
			for(var i:uint=0; i<_globalMarkers.length; i++)
			{
				if(_globalMarkers.getItemAt(i).time >= _count)
				{
					break;
				}
			}
			
			if(i==0)
			{
				_count = 0;	
			}else
			{
				_count = _globalMarkers.getItemAt(i-1).time;
			}
			 
			_increment = 0;				
		}
		
		/**
		 * Responds to a user clicking on the Stop button. 
		 * @param evt
		 * 
		 */
		protected function onStop(evt:Event):void
		{
			_increment = 0;				
		}
		
		/**
		 * Responds to a user clicking on the Play button. 
		 * @param evt
		 * 
		 */
		protected function onPlay(evt:Event):void
		{
			
			_increment = 1;
			
		}
		
		/**
		 * Responds to a user clicking on the Next button. 
		 * @param evt
		 * 
		 */
		protected function onNext(evt:Event):void
		{
			if(_globalMarkers.length == 0)
			{
				_count = _maxTime;
				_increment = 0;
				
				return;
			}
			
			if(_count < _globalMarkers.getItemAt(0).time)
			{
				_count = _globalMarkers.getItemAt(0).time;
				_increment = 0;
				
				return;
			}
			
			for(var i:uint=_globalMarkers.length; i>0; i--)
			{
				if(_globalMarkers.getItemAt(i-1).time <= _count)
				{
					break;
				} 
			}
			
			if(i >= _globalMarkers.length)
			{
				_count = _maxTime;
			}else
			{
				_count = _globalMarkers.getItemAt(i).time;
			}
			
			_increment = 0;
			
		}
		
		
		
		/**
		 * Responds to timer events. 
		 * @param evt
		 * 
		 */
		protected function onTimer(evt:Event):void
		{
	
			if(_count >= _maxTime)
			{
				_increment = 0;
			}
			
			var gm:GlobalMarkerVO;
			
			if(_increment)
			{
				for each(gm in _globalMarkers)
				{
					
					if(gm.time -1 == _count)
					{
						_count++;
						_increment = 0;
					}
				}
			}
			
			sendNotification(Notifications.PLAY, _count += _increment);
		}
		
		/**
		 * Returns typed reference to model. 
		 * @return 
		 * 
		 */
		protected function get settings():Animator3DPlayerProxy
		{
			var proxy:IProxy = facade.retrieveProxy(Animator3DPlayerProxy.NAME);
			
			return proxy as Animator3DPlayerProxy;
		}	
	}
}